package com.refusea.train.config.mvc

import com.alibaba.fastjson2.JSON
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.core.MethodParameter
import org.springframework.http.HttpInputMessage
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
import java.lang.reflect.Type

/**
 * 请求/响应日志
 *
 * @author Zhang Yan 2021年9月13日
 */
@ControllerAdvice(basePackages = ["com.refusea.train.controller"])
class LogAdvice : RequestBodyAdvice, ResponseBodyAdvice<Any> {

    override fun supports(
        methodParameter: MethodParameter,
        targetType: Type,
        converterType: Class<out HttpMessageConverter<*>>,
    ): Boolean {
        return true
    }

    override fun beforeBodyRead(
        inputMessage: HttpInputMessage,
        parameter: MethodParameter,
        targetType: Type,
        converterType: Class<out HttpMessageConverter<*>>,
    ): HttpInputMessage {
        return inputMessage
    }

    override fun afterBodyRead(
        body: Any,
        inputMessage: HttpInputMessage,
        parameter: MethodParameter,
        targetType: Type,
        converterType: Class<out HttpMessageConverter<*>>,
    ): Any {
        val method = parameter.method
        val name = method?.name
        if (debug) {
            log.debug(REQ, name, JSON.toJSONString(body))
        } else {
            log.info(REQ, name, JSON.toJSONString(body))
        }
        return body
    }

    override fun handleEmptyBody(
        body: Any?,
        inputMessage: HttpInputMessage,
        parameter: MethodParameter,
        targetType: Type,
        converterType: Class<out HttpMessageConverter<*>>,
    ): Any? {
        return body
    }

    override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
        return true
    }

    override fun beforeBodyWrite(
        body: Any?,
        returnType: MethodParameter,
        selectedContentType: MediaType,
        selectedConverterType: Class<out HttpMessageConverter<*>>,
        request: ServerHttpRequest,
        response: ServerHttpResponse,
    ): Any? {
        val method = returnType.method
        val name = method?.name
        if (debug) {
            log.debug(RSP, name, JSON.toJSONString(body))
        } else {
            log.info(RSP, name, JSON.toJSONString(body))
        }

        return body
    }

    companion object {
        private val log: Logger = LoggerFactory.getLogger(LogAdvice::class.java)
        private val debug = log.isDebugEnabled
        private const val REQ = "[{}] req={}"
        private const val RSP = "[{}] rsp={}"
    }
}
